﻿/*************************************************************************
Diagrams library
Copyright (c) 2010 Pavel Torgashov.

>>> LICENSE >>>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (www.fsf.org); either version 2 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

A copy of the GNU General Public License is available at
http://www.fsf.org/licensing/licenses

>>> END OF LICENSE >>>
*************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Drawing.Printing;

namespace Diagrams
{
    public partial class DiagramBox : UserControl
    {
        Diagram diagram;
        //выделенная фигура
        Figure selectedFigure = null;
        //фигура или маркер, который тащится мышью
        Figure draggedFigure = null;

        List<Marker> markers = new List<Marker>();
        Pen selectRectPen;

        public DiagramBox()
        {
            InitializeComponent();

            DoubleBuffered = true;
            ResizeRedraw = true;

            selectRectPen = new Pen(Color.Red, 1);
            selectRectPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
        }


        //диаграмма, отображаемая компонентом
        public Diagram Diagram
        {
            get { return diagram; }
            set { 
                diagram = value;
                selectedFigure = null;
                draggedFigure = null;
                markers.Clear();
                Invalidate();
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            Draw(e.Graphics);
        }

        private void Draw(Graphics gr)
        {
            gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            if (diagram != null)
            {
                //сначала рисуем соединительные линии
                foreach (Figure f in diagram.figures)
                    if (f is LineFigure)
                        f.Draw(gr);
                //затем рисуем плоские фигуры
                foreach (Figure f in diagram.figures)
                    if (f is SolidFigure)
                        f.Draw(gr);
            }

            //рисуем прямоугольник выделенной фигуры
            if (selectedFigure is SolidFigure)
            {
                SolidFigure figure = selectedFigure as SolidFigure;
                RectangleF bounds = figure.Bounds;
                gr.DrawRectangle(selectRectPen, bounds.Left - 2, bounds.Top - 2, bounds.Width + 4, bounds.Height + 4);
            }
            //рисуем маркеры
            foreach (Marker m in markers)
                m.Draw(gr);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            Focus();
            draggedFigure = FindFigureByPoint(e.Location);
            if (!(draggedFigure is Marker))
            {
                selectedFigure = draggedFigure;
                CreateMarkers();
            }
            startDragPoint = e.Location;
            Invalidate();

            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                if (selectedFigure == null)
                    cmMain.Show(PointToScreen(e.Location));
                else
                    cmSelectedFigure.Show(PointToScreen(e.Location));
            }
        }

        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            base.OnMouseDoubleClick(e);
            if (selectedFigure != null)
                editTextToolStripMenuItem_Click(null, null);
        }

        private void CreateMarkers()
        {
            if (selectedFigure == null)
                markers = new List<Marker>();
            else
            {
                markers = selectedFigure.CreateMarkers(diagram);
                UpdateMarkers();
            }
        }

        private void UpdateMarkers()
        {
            foreach (Marker m in markers)
                if(draggedFigure!=m)//маркер который тащится, обновляется сам
                    m.UpdateLocation();
        }

        Point startDragPoint;

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (draggedFigure != null && (draggedFigure is SolidFigure))
                {
                    (draggedFigure as SolidFigure).Offset(e.Location.X - startDragPoint.X, e.Location.Y - startDragPoint.Y);
                    UpdateMarkers();
                    Invalidate();
                }
            }
            else
            {
                Figure figure = FindFigureByPoint(e.Location);
                if (figure is Marker)
                    Cursor = Cursors.SizeAll;
                else
                if (figure != null)
                    Cursor = Cursors.Hand;
                else
                    Cursor = Cursors.Default;
            }

            startDragPoint = e.Location;
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            draggedFigure = null;
            UpdateMarkers();
            Invalidate();
        }

        //поиск фигуры, по данной точке
        Figure FindFigureByPoint(Point p)
        {
            //ищем среди маркеров
            foreach (Marker m in markers)
                if (m.IsInsidePoint(p))
                    return m;
            //затем ищем среди плоских фигур
            for (int i = diagram.figures.Count - 1; i >= 0; i--)
                if (diagram.figures[i] is SolidFigure && diagram.figures[i].IsInsidePoint(p))
                    return diagram.figures[i];
            //затем ищем среди линий
            for (int i = diagram.figures.Count - 1; i >= 0; i--)
                if (diagram.figures[i] is LineFigure && diagram.figures[i].IsInsidePoint(p))
                    return diagram.figures[i];
            return null;
        }

        private void miAddRect_Click(object sender, EventArgs e)
        {
            AddFigure<RectFigure>(startDragPoint);
        }

        private void AddFigure<FigureType>(Point location) where FigureType:SolidFigure
        {
            FigureType figure = Activator.CreateInstance<FigureType>();
            figure.location = location;
            if (diagram != null)
                diagram.figures.Add(figure);
            Invalidate();
        }

        private void addRoundRectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddFigure<RoundRectFigure>(startDragPoint);
        }

        private void addRhombToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddFigure<RhombFigure>(startDragPoint);
        }

        private void addParalelogrammToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddFigure<ParalelogrammFigure>(startDragPoint);
        }

        private void addEllipseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddFigure<EllipseFigure>(startDragPoint);
        }

        private void addStackToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddFigure<StackFigure>(startDragPoint);
        }

        private void addFrameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddFigure<FrameFigure>(startDragPoint);
        }

        private void bringToFrontToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (selectedFigure != null)
            {
                diagram.figures.Remove(selectedFigure);
                diagram.figures.Add(selectedFigure);
                Invalidate();
            }
        }

        private void sendToBackToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (selectedFigure != null)
            {
                diagram.figures.Remove(selectedFigure);
                diagram.figures.Insert(0,selectedFigure);
                Invalidate();
            }
        }

        private void editTextToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (selectedFigure != null && (selectedFigure is SolidFigure))
            {
                SolidFigure figure = (selectedFigure as SolidFigure);
                TextBox textBox = new TextBox();
                textBox.Parent = this;
                textBox.SetBounds(figure.TextBounds.Left, figure.TextBounds.Top, figure.TextBounds.Width, figure.TextBounds.Height);
                textBox.Text = figure.text;
                textBox.Multiline = true;
                textBox.TextAlign = HorizontalAlignment.Center;
                textBox.Focus();
                textBox.LostFocus += new EventHandler(textBox_LostFocus);
            }
            else if (selectedFigure != null && (selectedFigure is LineFigure))
            {
                LineFigure figure = (selectedFigure as LineFigure);
                NumericTextBox textBox = new NumericTextBox();
                textBox.Parent = this;
                textBox.SetBounds(figure.TextBounds.Left, figure.TextBounds.Top, figure.TextBounds.Width, figure.TextBounds.Height);
                textBox.Text = figure.text;
                textBox.Multiline = false;
                textBox.TextAlign = HorizontalAlignment.Center;
                textBox.Focus();
                textBox.LostFocus += new EventHandler(textBox_LostFocus);
            }
        }

        private void OnSetFistFigure(object sender, EventArgs e)
        {
            if (selectedFigure != null && (selectedFigure is SolidFigure))
            {
                diagram.SetFistFigure(selectedFigure);
                this.Refresh();
            }
        }

        private void OnSetLastFigure(object sender, EventArgs e)
        {
            if (selectedFigure != null && (selectedFigure is SolidFigure))
            {
                diagram.SetLastFigure(selectedFigure);
                this.Refresh();
            }
        }

        void textBox_LostFocus(object sender, EventArgs e)
        {
            if (selectedFigure != null && (selectedFigure is SolidFigure))
            {
                (selectedFigure as SolidFigure).text = (sender as TextBox).Text;
            }
            else if (selectedFigure != null && (selectedFigure is LineFigure))
            {
                (selectedFigure as LineFigure).text = (sender as TextBox).Text;
            }
            Controls.Remove((Control)sender);
        }

        private void miAddLine_Click(object sender, EventArgs e)
        {
            if (selectedFigure != null && (selectedFigure is SolidFigure))
            {
                LineFigure line = new LineFigure();
                line.From = (selectedFigure as SolidFigure);
                EndLineMarker marker = new EndLineMarker(diagram, 1);
                marker.location = line.From.location;
                marker.location.Offset(0, (int)line.From.Size.Height/2+10);
                line.To = marker;
                diagram.figures.Add(line);
                selectedFigure = line;
                CreateMarkers();
                
                Invalidate();
            }
        }

        private void miAddLedgeLine_Click(object sender, EventArgs e)
        {
            if (selectedFigure != null && (selectedFigure is SolidFigure))
            {
                LedgeLineFigure line = new LedgeLineFigure();
                line.From = (selectedFigure as SolidFigure);
                EndLineMarker marker = new EndLineMarker(diagram, 1);
                marker.location = line.From.location;
                marker.location.Offset(0, (int)line.From.Size.Height / 2 + 10);
                line.To = marker;
                diagram.figures.Add(line);
                selectedFigure = line;
                CreateMarkers();

                Invalidate();
            }
        }

        private void miDelete_Click(object sender, EventArgs e)
        {
            if (selectedFigure != null)
            {
                //удалем фигуру
                diagram.figures.Remove(selectedFigure);

                //удялаем также все линии, ведущие к данной фигуре
                for(int i = diagram.figures.Count-1;i>=0;i--)
                    if (diagram.figures[i] is LineFigure)
                    {
                        LineFigure line = (diagram.figures[i] as LineFigure);
                        if (line.To == selectedFigure || line.From == selectedFigure)
                            diagram.figures.RemoveAt(i);
                    }

                selectedFigure = null;
                draggedFigure = null;
                CreateMarkers();

                Invalidate();
            }
        }

        protected override void  OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
                miDelete_Click(null, null);

            base.OnKeyDown(e);
        }

        //преобразуем в картинку
        public Bitmap GetImage()
        {
            selectedFigure = null;
            draggedFigure = null;
            CreateMarkers();

            Bitmap bmp = new Bitmap(Bounds.Width, Bounds.Height);
            DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));

            return bmp;
        }


        //сохраняем как метафайл
        public void SaveAsMetafile(string fileName)
        {
            selectedFigure = null;
            draggedFigure = null;
            CreateMarkers();

            Metafile curMetafile = null;
            Graphics g = this.CreateGraphics();
            IntPtr hdc = g.GetHdc();
            Rectangle rect = new Rectangle(0, 0, 200, 200);
            try
            {
                curMetafile =
                    new Metafile(fileName, hdc, System.Drawing.Imaging.EmfType.EmfOnly);
            }
            catch
            {
                g.ReleaseHdc(hdc);
                g.Dispose();
                throw;
            }

            Graphics gr = Graphics.FromImage(curMetafile);
            //отрисовываем диаграмму
            Draw(gr);

            g.ReleaseHdc(hdc);
            gr.Dispose();
            g.Dispose();
        }
    }
}
